package net.chrisrichardson.ec2deployer.cluster

import net.chrisrichardson.ec2deployer.applications.SchemaSpecification
import net.chrisrichardson.ec2deployer.core.EC2InstanceType
import org.apache.commons.logging.*
import net.chrisrichardson.ec2deployer.applications.WebApp

/**
Define the configuration of a cluster. How many servers and of what type
*/

public class ClusterSpec implements Serializable {

    private Log logger = LogFactory.getLog(getClass())

    private long id
    int numberOfTomcats
    List<WebApp> webApps = []
    def catalinaOptsBuilder
    SchemaSpecification schemaSpec
    int numberOfSlaves = 0
    int numberOfTerracottaHosts = 0
    String bucketPrefix = ""

    String masterDatabaseInstanceType = EC2InstanceType.SMALL
    String slaveDatabaseInstanceType = EC2InstanceType.SMALL
    String tomcatInstanceType = EC2InstanceType.SMALL
    String apacheInstanceType = EC2InstanceType.SMALL
    String jmeterInstanceType = EC2InstanceType.SMALL
    String topology = MultipleInstancesTopology.simpleName

    boolean sslRequired
    String sslKeyFile
    String sslCertFile

    String staticContentBucket
    String staticContentPath

    String securityGroupPrefix

    String extraHttpConf


    String externalIpAddress


    /**
    Whether to use a slave in a different availability zone
    **/
    
    boolean haSlave = false

    /*
        The number of Tomcat servers
    */

    EbsVolume ebsVolume

    def tomcats(int numberOfTomcats) {
        this.numberOfTomcats = numberOfTomcats
        this
    }

    /**
        Adds a web application to deploy to each of the tomcats
    */
    
    def webApp(String pathToExplodedWar, String context) {
        webApps.add(new WebApp(pathToExplodedWar, context))
        this
    }

    /**
      The builder that creates the CATALINA_OPTS that are used to start tomcat
    */

    def catalinaOptsBuilder(catalinaOptsBuilder) {
        this.catalinaOptsBuilder = catalinaOptsBuilder
        this
    }

    /**
        The name of the database schema, the users, and the SQL scripts to execute to initialize the database    
    */

    def schema(String schemaName, Map<String, String> users, List<String> scripts = []) {
        this.schemaSpec = new SchemaSpecification(schemaName, users, scripts)
        this
    }

    /**
      The number of MySQL replication slaves
    */

    def slaves(int numberOfSlaves) {
        this.numberOfSlaves = numberOfSlaves
        this
    }

    def getAllInstancesSameType() {
        new HashSet([masterDatabaseInstanceType, slaveDatabaseInstanceType, tomcatInstanceType, apacheInstanceType, jmeterInstanceType]).size() == 1
    }

    def getInstanceType() {
        masterDatabaseInstanceType
    }

    /**
      The Amazon AMI type (small, large, and extra large) to launch for the database server
    */

    def masterDatabaseInstanceType(type) {
        masterDatabaseInstanceType = type
        this
    }

    /**
      The Amazon AMI type to launch for the database slaves
    */

    def slaveDatabaseInstanceType(type) {
        slaveDatabaseInstanceType = type
        this
    }

    /**
      The Amazon AMI type (small, large, and extra large) to launch for the tomcat servers
    */

    def tomcatInstanceType(type) {
        tomcatInstanceType = type
        this
    }

    /**
      The Amazon AMI type (small, large, and extra large) to launch for the Apache server
    */


    def apacheInstanceType(type) {
        apacheInstanceType= type
        this
    }

    /**
      The Amazon AMI type (small, large, and extra large) to launch for the Jmeter server
    */


    def jmeterInstanceType(type) {
        jmeterInstanceType = type
        this
    }

    /**
      The default Amazon AMI type (small, large, and extra large) for all of the servers 
    */


    ClusterSpec instanceType(type) {
        masterDatabaseInstanceType = type
        slaveDatabaseInstanceType = type
        tomcatInstanceType = type
        apacheInstanceType= type
        jmeterInstanceType = type
        this
    }

    ClusterSpec topology(type) {
        topology = type
        this
    }

    ClusterSpec haSlave() {
        haSlave = true
        this
    }

    def getCatalinaOptsBuilderAsClosure() {
        catalinaOptsBuilder instanceof Closure ? catalinaOptsBuilder : toClosure((String)catalinaOptsBuilder)
    }

    def toClosure(String closureString) {
        def shell = new GroovyShell()
        logger.debug "Evaluating: " + closureString
        def result = shell.evaluate(closureString)
        logger.debug "result: " + result
        result
    }

   ClusterTopology makeTopology() {
       topologyClass.newInstance()
   }

   def getTopologyClass() {
       if (topology.indexOf('.') >= 0)
        return Class.forName(topology)
       Class.forName("net.chrisrichardson.ec2deployer.cluster." + topology)
   }

   boolean getUseHaSlave() {
       haSlave && numberOfSlaves > 0
   }

    def terracottaHosts(int n) {
        this.numberOfTerracottaHosts = n
        this
    }

    def bucketPrefix(String prefix) {
        bucketPrefix = prefix
        this
    }

    ClusterSpec ssl(String keyFile, String certFile) {
        this.sslRequired = true
        this.sslKeyFile = keyFile
        this.sslCertFile = certFile
        this
    }

    boolean getStaticContentSupplied() {
        staticContentPath || staticContentBucket
    }

    ClusterSpec staticContent(String path, String bucket) {
        this.staticContentPath = path
        this.staticContentBucket = bucket
        this
    }

    ClusterSpec withSecurityGroupPrefix(String securityGroupPrefix) {
        this.securityGroupPrefix = securityGroupPrefix
        this
    }

    ClusterSpec extraHttpConf(String conf) {
        this.extraHttpConf = conf
        this
    }

    def externalIpAddress(String externalIpAddress) {
      this.externalIpAddress = externalIpAddress
      this
    }

    ClusterSpec withExistingEbsVolume(String volumeId, String device) {
        this.ebsVolume = EbsVolume.existingVolume(volumeId, device)
        this
    }

    ClusterSpec withNewEbsVolume(int size, String device) {
        this.ebsVolume = EbsVolume.newVolume(size, device)
        this
    }

    ClusterSpec withEbsVolumeFromSnapshot(String snapshotId, Integer size, String device) {
        this.ebsVolume = EbsVolume.newVolumeFromSnapshot(snapshotId, size, device)
        this
    }

    EbsVolume getEbsVolume() {
        ebsVolume?.device ? ebsVolume : null
    }


    boolean getMustCreateDb() {
        ebsVolume == null ||  ebsVolume.mustCreateDb
    }


}